home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / HTMIME.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  12.1 KB  |  484 lines

  1. /*            MIME Message Parse            HTMIME.c
  2. **            ==================
  3. **
  4. **    This is RFC 1341-specific code.
  5. **    The input stream pushed into this parser is assumed to be
  6. **    stripped on CRs, ie lines end with LF, not CR LF.
  7. **    (It is easy to change this except for the body part where
  8. **    conversion can be slow.)
  9. **
  10. ** History:
  11. **       Feb 92    Written Tim Berners-Lee, CERN
  12. **
  13. */
  14. #include "HTUtils.h"
  15. #include "HTMIME.h"        /* Implemented here */
  16. #include "HTAlert.h"
  17.  
  18. #include "LYLeaks.h"
  19.  
  20.  
  21. /*        MIME Object
  22. **        -----------
  23. */
  24.  
  25. PUBLIC char * redirecting_url=NULL;
  26. extern int loading_length; /* from HTFormat.c (for HTCopy) */
  27.  
  28. typedef enum _MIME_state {
  29.     MIME_TRANSPARENT,    /* put straight through to target ASAP! */
  30.     BEGINNING_OF_LINE,
  31.     CONTENT_,
  32.     CONTENT_ENCODING,
  33.     CONTENT_LENGTH,
  34.     CONTENT_T,
  35.     CONTENT_TRANSFER_ENCODING,
  36.     CONTENT_TYPE,
  37.     SKIP_GET_VALUE,        /* Skip space then get value */
  38.     GET_VALUE,        /* Get value till white space */
  39.     JUNK_LINE,        /* Ignore the rest of this folded line */
  40.     NEWLINE,        /* Just found a LF .. maybe continuation */
  41.     CHECK,            /* check against check_pointer */
  42.     MIME_NET_ASCII,        /* Translate from net ascii */
  43.     MIME_IGNORE,        /* ignore entire file */
  44.     LOCATION        /* different URL */
  45.     /* TRANSPARENT and IGNORE are defined as stg else in _WINDOWS */
  46. } MIME_state;
  47.  
  48. #define VALUE_SIZE 1024        /* @@@@@@@ Arbitrary? */
  49. struct _HTStream {
  50.     CONST HTStreamClass *    isa;
  51.     
  52.     BOOL            net_ascii;    /* Is input net ascii? */
  53.     MIME_state        state;        /* current state */
  54.     MIME_state        if_ok;        /* got this state if match */
  55.     MIME_state        field;        /* remember which field */
  56.     MIME_state        fold_state;    /* state on a fold */
  57.     CONST char *        check_pointer;    /* checking input */
  58.     
  59.     char *            value_pointer;    /* storing values */
  60.     char             value[VALUE_SIZE];
  61.     
  62.     HTParentAnchor *    anchor;        /* Given on creation */
  63.     HTStream *        sink;        /* Given on creation */
  64.     
  65.     char *            boundary;    /* For multipart */
  66.     
  67.     HTFormat        encoding;    /* Content-Transfer-Encoding */
  68.     char *            compression_encoding;
  69.     int            content_length;
  70.     HTFormat        format;        /* Content-Type */
  71.     HTStream *        target;        /* While writing out */
  72.     HTStreamClass        targetClass;
  73.     
  74.     HTAtom *        targetRep;    /* Converting into? */
  75. };
  76.  
  77.  
  78. /*_________________________________________________________________________
  79. **
  80. **            A C T I O N     R O U T I N E S
  81. */
  82.  
  83. /*    Character handling
  84. **    ------------------
  85. **
  86. **    This is a FSM parser which is tolerant as it can be of all
  87. **    syntax errors.  It ignores field names it does not understand,
  88. **    and resynchronises on line beginnings.
  89. */
  90.  
  91. PRIVATE void HTMIME_put_character ARGS2(HTStream *, me, char, c)
  92. {
  93.     if (me->state == MIME_TRANSPARENT) {
  94.         (*me->targetClass.put_character)(me->target, c);/* MUST BE FAST */
  95.     return;
  96.     }
  97.     
  98.     /* This slightly simple conversion just strips CR and turns LF to
  99.     ** newline. On unix LF is \n but on Mac \n is CR for example.
  100.     ** See NetToText for an implementation which preserves single CR or LF.
  101.     */
  102.     if (me->net_ascii) {
  103.         c = FROMASCII(c);
  104.     if (c == CR) return;
  105.     else if (c == LF) c = '\n';
  106.     }
  107.     
  108.     switch(me->state) {
  109.  
  110.     case MIME_IGNORE:
  111.         return;
  112.  
  113.     case MIME_TRANSPARENT:        /* Not reached see above */
  114.         (*me->targetClass.put_character)(me->target, c);
  115.     return;
  116.     
  117.     case MIME_NET_ASCII:
  118.         (*me->targetClass.put_character)(me->target, c); /* MUST BE FAST */
  119.     return;
  120.  
  121.     case NEWLINE:
  122.     if (c != '\n' && WHITE(c)) {        /* Folded line */
  123.         me->state = me->fold_state;    /* pop state before newline */
  124.         break;
  125.     }
  126.     
  127.     /*    else Falls through */
  128.     
  129.     case BEGINNING_OF_LINE:
  130.         switch(c) {
  131.     case 'c':
  132.     case 'C':
  133.         me->check_pointer = "ontent-";
  134.         me->if_ok = CONTENT_;
  135.         me->state = CHECK;
  136.         break;
  137.     case 'l':
  138.         case 'L':
  139.           me->check_pointer = "ocation:";
  140.           me->if_ok = LOCATION;
  141.           me->state = CHECK;
  142.           if (TRACE)
  143.             fprintf (stderr,
  144.                      "[MIME] Got L at beginning of line\n");
  145.           break;
  146.  
  147.     case '\n':            /* Blank line: End of Header! */
  148.         {
  149.             if (TRACE) fprintf(stderr,
  150.             "HTMIME: MIME content type is %s, converting to %s\n",
  151.             HTAtom_name(me->format), HTAtom_name(me->targetRep));
  152.         me->target = HTStreamStack(me->format, me->targetRep,
  153.              me->sink , me->anchor);
  154.         if (!me->target) {
  155.             if (TRACE) fprintf(stderr, "MIME: Can't translate! ** \n");
  156.             me->target = me->sink;    /* Cheat */
  157.         }
  158.         if (me->target) {
  159.             me->targetClass = *me->target->isa;
  160.         /* Check for encoding and select state from there @@ */
  161.         
  162.             me->state = MIME_TRANSPARENT; /* From now push straigh through */
  163.         } else {
  164.             me->state = MIME_IGNORE;        /* What else to do? */
  165.         }
  166.         }
  167.         break;
  168.         
  169.     default:
  170.        goto bad_field_name;
  171.        break;
  172.        
  173.     } /* switch on character */
  174.         break;
  175.     
  176.     case CHECK:                /* Check against string */
  177.         if (TOLOWER(c) == *(me->check_pointer)++) {
  178.         if (!*me->check_pointer) me->state = me->if_ok;
  179.     } else {        /* Error */
  180.         if (TRACE) fprintf(stderr,
  181.             "HTMIME: Bad character `%c' found where `%s' expected\n",
  182.         c, me->check_pointer - 1);
  183.         goto bad_field_name;
  184.     }
  185.     break;
  186.     
  187.     case CONTENT_:
  188.     if (TRACE)
  189.            fprintf (stderr,
  190.                  "[MIME] in case CONTENT_\n");
  191.         switch(c) {
  192.         case 't':
  193.         case 'T':
  194.           me->state = CONTENT_T;
  195.           if (TRACE)
  196.             fprintf (stderr,
  197.                      "[MIME] Was CONTENT_, found T, state now CONTENT_T\n");
  198.           break;
  199.  
  200.         case 'e':
  201.         case 'E':
  202.           me->check_pointer = "ncoding:";
  203.           me->if_ok = CONTENT_ENCODING;
  204.           me->state = CHECK;
  205.           if (TRACE)
  206.             fprintf (stderr,
  207.                      "[MIME] Was CONTENT_, found E, checking for 'ncoding:'\n");
  208.           break;
  209.  
  210.         case 'l':
  211.         case 'L':
  212.           me->check_pointer = "ength:";
  213.           me->if_ok = CONTENT_LENGTH;
  214.           me->state = CHECK;
  215.           if (TRACE)
  216.             fprintf (stderr,
  217.                      "[MIME] Was CONTENT_, found L, checking for 'ength:'\n");
  218.           break;
  219.  
  220.         default:
  221.           if (TRACE)
  222.             fprintf (stderr,
  223.                      "[MIME] Was CONTENT_, found nothing; bleah\n");
  224.           goto bad_field_name;
  225.  
  226.         } /* switch on character */
  227.       break;
  228.  
  229.     case CONTENT_T:
  230.       if (TRACE)
  231.         fprintf (stderr,
  232.                  "[MIME] in case CONTENT_T\n");
  233.       switch(c) {
  234.     case 'r':
  235.     case 'R':
  236.         me->check_pointer = "ansfer-encoding:";
  237.         me->if_ok = CONTENT_TRANSFER_ENCODING;
  238.         me->state = CHECK;
  239.         break;
  240.         
  241.     case 'y':
  242.     case 'Y':
  243.         me->check_pointer = "pe:";
  244.         me->if_ok = CONTENT_TYPE;
  245.         me->state = CHECK;
  246.         break;
  247.         
  248.     default:
  249.         goto bad_field_name;
  250.         
  251.     } /* switch on character */
  252.     break;
  253.     
  254.     case CONTENT_TYPE:
  255.     case CONTENT_TRANSFER_ENCODING:
  256.     case CONTENT_ENCODING:
  257.     case CONTENT_LENGTH:
  258.     case LOCATION:
  259.         me->field = me->state;        /* remember it */
  260.     me->state = SKIP_GET_VALUE;
  261.                 /* Fall through! */
  262.     case SKIP_GET_VALUE:
  263.         if (c == '\n') {
  264.        me->fold_state = me->state;
  265.        me->state = NEWLINE;
  266.        break;
  267.     }
  268.     if (WHITE(c)) break;    /* Skip white space */
  269.     
  270.     me->value_pointer = me->value;
  271.     me->state = GET_VALUE;   
  272.     /* Fall through to store first character */
  273.     
  274.     case GET_VALUE:
  275.         if (WHITE(c)) {            /* End of field */
  276.         *me->value_pointer = 0;
  277.         switch (me->field) {
  278.         case CONTENT_TYPE:
  279.             { /* force the Content-type value to all lowercase */
  280.           char *cp;
  281.           for(cp=(char *)me->value; *cp; cp++)
  282.               *cp = TOLOWER(*cp);
  283.         }
  284.             me->format = HTAtom_for(me->value);
  285.         break;
  286.         case CONTENT_TRANSFER_ENCODING:
  287.             me->encoding = HTAtom_for(me->value);
  288.         break;
  289.             case CONTENT_ENCODING:
  290.         me->compression_encoding=0;
  291.                 StrAllocCopy(me->compression_encoding, me->value);
  292.         
  293.                 if (TRACE)
  294.                   fprintf (stderr,
  295.                        "[MIME_put_char] Picked up compression encoding '%s'\n",
  296.                         me->compression_encoding);
  297.               break;
  298.         case CONTENT_LENGTH:
  299.                 me->content_length = atoi (me->value);
  300.                 /* This is TEMPORARY. */
  301.                 loading_length = me->content_length;
  302.                 if (TRACE)
  303.                   fprintf (stderr,
  304.                          "[MIME_put_char] Picked up content length '%d'\n",
  305.                          me->content_length);
  306.                 break;
  307.         case LOCATION:
  308.         StrAllocCopy(redirecting_url, me->value);
  309.                 if (TRACE)
  310.                    fprintf(stderr,
  311.                      "[MIME_put_char] Picked up location '%s'\n", me->value);
  312.                 break;
  313.         default:        /* Should never get here */
  314.             break;
  315.         }
  316.     } else {
  317.         if (me->value_pointer < me->value + VALUE_SIZE - 1) {
  318.             *me->value_pointer++ = c;
  319.         break;
  320.         } else {
  321.             goto value_too_long;
  322.         }
  323.     }
  324.     /* Fall through */
  325.     
  326.     case JUNK_LINE:
  327.         if (c == '\n') {
  328.         me->state = NEWLINE;
  329.         me->fold_state = me->state;
  330.     }
  331.     break;
  332.     
  333.     
  334.     } /* switch on state*/
  335.     
  336.     return;
  337.     
  338. value_too_long:
  339.     if (TRACE) fprintf(stderr,
  340.         "HTMIME: *** Syntax error. (string too long)\n");
  341.     
  342. bad_field_name:                /* Ignore it */
  343.     me->state = JUNK_LINE;
  344.     return;
  345.     
  346. }
  347.  
  348.  
  349.  
  350. /*    String handling
  351. **    ---------------
  352. **
  353. **    Strings must be smaller than this buffer size.
  354. */
  355. PRIVATE void HTMIME_put_string ARGS2(HTStream *, me, CONST char*, s)
  356. {
  357.     CONST char * p;
  358.  
  359.     if(TRACE)    {
  360.         fprintf(stderr, "MIME:  %s\n", s);
  361.     }
  362.  
  363.     if (me->state == MIME_TRANSPARENT)        /* Optimisation */
  364.         (*me->targetClass.put_string)(me->target,s);
  365.     else if (me->state != MIME_IGNORE)
  366.         for (p=s; *p; p++) HTMIME_put_character(me, *p);
  367. }
  368.  
  369.  
  370. /*    Buffer write.  Buffers can (and should!) be big.
  371. **    ------------
  372. */
  373. PRIVATE void HTMIME_write ARGS3(HTStream *, me, CONST char*, s, int, l)
  374. {
  375.     CONST char * p;
  376.  
  377.         if(TRACE)       {
  378.                 fprintf(stderr, "MIME:  %s\n", s);
  379.         }
  380.  
  381.     if (me->state == MIME_TRANSPARENT)        /* Optimisation */
  382.         (*me->targetClass.put_block)(me->target, s, l);
  383.     else
  384.         for (p=s; p < s+l; p++) HTMIME_put_character(me, *p);
  385. }
  386.  
  387.  
  388.  
  389.  
  390. /*    Free an HTML object
  391. **    -------------------
  392. **
  393. */
  394. PRIVATE void HTMIME_free ARGS1(HTStream *, me)
  395. {
  396.     if (me->target) (*me->targetClass._free)(me->target);
  397.     free(me);
  398. }
  399.  
  400. /*    End writing
  401. */
  402.  
  403. PRIVATE void HTMIME_abort ARGS2(HTStream *, me, HTError, e)
  404. {
  405.     if (me->target) (*me->targetClass._abort)(me->target, e);
  406.     free(me);
  407. }
  408.  
  409.  
  410.  
  411. /*    Structured Object Class
  412. **    -----------------------
  413. */
  414. PRIVATE CONST HTStreamClass HTMIME =
  415. {        
  416.     "MIMEParser",
  417.     HTMIME_free,
  418.     HTMIME_abort,
  419.     HTMIME_put_character,
  420.     HTMIME_put_string,
  421.     HTMIME_write
  422. }; 
  423.  
  424.  
  425. /*    Subclass-specific Methods
  426. **    -------------------------
  427. */
  428.  
  429. PUBLIC HTStream* HTMIMEConvert ARGS3(
  430.     HTPresentation *,    pres,
  431.     HTParentAnchor *,    anchor,
  432.     HTStream *,        sink)
  433. {
  434.     HTStream* me;
  435.     
  436.     me = (HTStream *)malloc(sizeof(*me));
  437.     if (me == NULL) outofmem(__FILE__, "HTML_new");
  438.     me->isa = &HTMIME;       
  439.  
  440.     me->sink =         sink;
  441.     me->anchor =     anchor;
  442.     me->target =     NULL;
  443.     me->state =     BEGINNING_OF_LINE;
  444.     /*
  445.      *    Sadly enough, change this to always default to WWW_HTML
  446.      *    to parse all text as HTML for the users.
  447.      *  GAB 06-30-94
  448.      *    Thanks to Robert Rowland robert@cyclops.pei.edu
  449.      *
  450.      *    After discussion of the correct handline, should be application/octet-
  451.      *        stream or unknown; causing servers to send a correct content
  452.      *        type.
  453.      *
  454.      *  The consequence of using WWW_UNKNOWN is that you end up downloading
  455.      *  as a binary file what 99.9% of the time is an HTML file, which should
  456.      *  have been rendered or displayed.  So sadly enough, I'm changing it
  457.      *  back to WWW_HTML, and it will handle the situation like Mosaic does,
  458.      *  and as Robert Rowland suggested, because being functionally correct
  459.      *  99.9% of the time is better than being technically correct but
  460.      *  functionally nonsensical. - FM
  461.      *//***
  462.     me->format =     WWW_UNKNOWN;
  463.         ***/
  464.     me->format =     WWW_HTML;
  465.     me->targetRep =     pres->rep_out;
  466.     me->boundary =     0;        /* Not set yet */
  467.     me->net_ascii =     NO;    /* Local character set */
  468.     return me;
  469. }
  470.  
  471. PUBLIC HTStream* HTNetMIME ARGS3(
  472.     HTPresentation *,    pres,
  473.     HTParentAnchor *,    anchor,
  474.     HTStream *,        sink)
  475. {
  476.     HTStream* me = HTMIMEConvert(pres,anchor, sink);
  477.     if (!me) return NULL;
  478.     
  479.     me->net_ascii = YES;
  480.     return me;
  481. }
  482.  
  483.  
  484.